﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using AcctrueWMS.Foundation;
using AcctrueWMS.Foundation.Data;
using AcctrueWMS.Foundation.Web;
using AcctrueWMS.Foundation.Common;
using AcctrueWMS.Foundation.DAL;
using System.Data;
using System.Xml;
using System.Text.RegularExpressions;
using System.Reflection;
using AcctrueWMS.Foundation.Util;




namespace AcctrueWMS.Foundation.Rule
{
    public static class ModelRuleRuntime
    {
        private static Collection<ModelRule> modelRules = null;
        private static IModelRuleManager modelRuleManager = new ModelRuleManager();
        private static int iProcessState;
        /// <summary>
        /// 扫描规则引擎文件XML
        /// </summary>
        /// 
        static IDataModelContext context;
        private static object objhelper = new object();
        public static IDataModelContext Context
        {
            get
            {
                if (context == null)
                {
                    lock (objhelper)
                    {
                        if (context == null)
                        {
                            context = new DataModelContext();
                            context.FilePath = SysRuleConst.modelFilePath;
                        }
                    }
                }
                return context;
            }
        }

        public static void Scan()
        {
            try
            {
                //加载XML         
                CreateModelRules();
                //首先处理上次处理失败的任务
                ScanningErrorData();
                //扫描新数据
                foreach (ModelRule rule in modelRules)
                {
                    DataTable dt = FindScanningRows(rule );
                    try
                    {
                        LoggerManager.GetLogger().Info(rule.DisplayName + "开始扫描");
                                    
                        if (dt != null)
                        {             
                            ProcessRule(dt, rule, "new");

                            string insertSQL = string.Empty;
                            insertSQL = string.Format("insert into sys_ren_scanRecord (lastScanTime,tableName,ruleName) values (getdate(),'{0}','{1}')", rule.ModelName, rule.Name);
                            WMSqlExcuter.ExecuteNonQuery(insertSQL);
                        }
                        LoggerManager.GetLogger().Info(rule.DisplayName + "结束扫描");
                    }
                    catch (Exception ex)
                    {
                        LoggerManager.GetLogger().Error(string.Format("{0} {1}", rule.Name, "处理时发生异常"), ex);
                       throw ex;
                    }
                }
            }
            catch (Exception ex)
            {
                LoggerManager.GetLogger().Error("扫描秒初始化报错！", ex);
               throw ex;
            }
        }
        //加载xml中定义的规则
        private static void CreateModelRules()
        {
            XmlDataDocument xmlDoc = new XmlDataDocument();
            try
            {
                //xmlDoc.Load(SysRuleConst.ruleFilePath);
                string fileName = AppDomain.CurrentDomain.BaseDirectory + "rules.xml";
                xmlDoc.Load(fileName);
                modelRules = new Collection<ModelRule>();
                ModelRule rule;
                XmlNodeList nodeList = xmlDoc.GetElementsByTagName("rule");
                string userfor = string.Empty;
                string processor = string.Empty;
                foreach (XmlNode node in nodeList)
                {
                    rule = new ModelRule();
                    rule.Name = node.Attributes["name"].Value;
                    rule.DisplayName = node.Attributes["displayName"].Value;
                    rule.Comment = node.Attributes["comment"].Value;
                    rule.Status = node.Attributes["status"].Value;
                    rule.ScanningType = node.Attributes["scanningType"].Value;
                    rule.Interval = node.Attributes["interval"] == null ? "" : node.Attributes["interval"].Value;
                    rule.Where = node.Attributes["where"] == null ? "" : node.Attributes["where"].Value;

                    string type = node.Attributes["type"].Value;
                    if (type == "Alert")
                    {
                        rule.RuleType = ModelRuleTypeEnum.Alert;
                    }
                    else
                    {
                        rule.RuleType = ModelRuleTypeEnum.TaskSplit;
                    }
                    userfor = node.Attributes["usefor"].Value;
                    rule.ModelName = userfor;
                    processor = node.Attributes["processor"].Value;
                    rule.Processor = CreateIRuleEventProcessor(userfor, processor);
                    modelRules.Add(rule);
                }
            }
            catch (Exception ex)
            {
                LoggerManager.GetLogger().Error("加载规则引擎配置文件发生异常！", ex);
                throw ex;
            }
        }
        //反射出IRuleEventProcessor 实体
        private static IRuleEventProcessor CreateIRuleEventProcessor(string modeName, string assemblyName)
        {
            if (string.IsNullOrEmpty(modeName) || string.IsNullOrEmpty(modeName))
            {
                throw new AcctrueWMS.Foundation.Common.Exception.WMSSysException("规则初始化失败！");
            }
            IRuleEventProcessor processor;
            try
            {
                //反射生成 processor 实例
                processor = CommandAnalyser.GetEntityByAssemblyName(assemblyName) as IRuleEventProcessor;
                return processor;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        //扫描 处理失败的记录
        private static void ScanningErrorData()
        {
            try
            {
                string errorDataSQL = "select  tableName,dataKey,ruleName,retryTimes  from  sys_ren_queue  order by  priority asc ,inTime  asc,  ruleName ";
                DataTable dt = WMSqlExcuter.ExecuteRealDataTable(errorDataSQL);
                string lastRuleName = "", ruleName;
                ModelRule rule = new ModelRule();
                //DataModel model;
                DataTable dtError;
                for (int i = 0; i < dt.Rows.Count; i++)
                {
                    ruleName = dt.Rows[i]["ruleName"].ToString();
                    if (lastRuleName != ruleName)
                    {
                        lastRuleName = ruleName;
                        rule = GetRuleByName(ruleName);
                    }
                    dtError = GetDateModeByTbName_Key(dt.Rows[i]["tableName"].ToString(), dt.Rows[i]["dataKey"].ToString());
                    ProcessRule(dtError, rule, "old");
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        //根据tableName 和dataKey获取表数据
        private static DataTable GetDateModeByTbName_Key(string tableName, string dataKey)
        {
            string where = string.Empty;
            //DataModel model = new DataModel(Context, tableName);
            //string[] keyNames = model.KeyColumnName.Split(',');
            string[] keyNames = getUnionKyes(tableName).Split('|');
            string[] dataKeyValues = dataKey.Split('|');
            for (int i = 0; i < keyNames.Length && i < dataKeyValues.Length; i++)
            {
                where += string.Format(" and  {0}='{1}' ", keyNames[i], dataKeyValues[i]);
            }
            string dataSQL = string.Format("select * from {0} where (1=1)  {1} ", tableName, where);
            DataTable dt = WMSqlExcuter.ExecuteRealDataTable(dataSQL);
            //model.FromDataTable(dt);
            return dt;
        }

        //根据ruleName产生一个ModelRule 实例
        private static ModelRule GetRuleByName(string RuleNmae)
        {
            foreach (ModelRule rule in modelRules)
            {
                if (rule.Name.ToUpper() == RuleNmae.ToUpper())
                {
                    return rule;
                }
            }
            return null;
        }
        //获取上次扫描时间
        private static string GetlastScanningTime(string tableName ,string ruleName)
        {
            string lastScannigTimeSQL = string.Format(" SELECT top 1   lastScanTime  FROM  sys_ren_scanRecord  where tableName='{0}' AND ruleName='{1}' ORDER BY  lastScanTime DESC ", tableName, ruleName);
            object getLastScanningObj = WMSqlExcuter.ExecuteScalar(lastScannigTimeSQL);
            if (getLastScanningObj != null && getLastScanningObj.ToString() != "")
            {
                return getLastScanningObj.ToString();
            }
            else
            {
                return string.Empty;
            }
        }

        //扫描数据
        public static DataTable FindScanningRows(ModelRule rule)
        {
            if (string.IsNullOrEmpty(rule.ModelName)) return null;
            string findSQL = string.Empty;
            string getLastScanningObj = GetlastScanningTime(rule.ModelName, rule.Name);
            switch (rule.ScanningType.ToUpper())
            {
                case "BYTIME":
                    {
                        if (!string.IsNullOrEmpty(getLastScanningObj) && rule.ScanningType.ToUpper() == "BYTIME")
                        {
                            string where =string.Format(rule.Where,Convert.ToDateTime(getLastScanningObj));
                            findSQL = string.Format("select * from  {0} where {1} ; ", rule.ModelName, where);
                        }
                        else
                        {
                            findSQL = string.Format("select * from  {0}  ;", rule.ModelName);
                        }
                        break;
                    }
                case "WHOLETABLE":
                    {
                        IDBOperator dbOperator = DBFactory.CreateDBOperator();
                        DateTime serverDate = dbOperator.GetDBDateTime();

                        string[] intervals = rule.Interval.Split(':');
                        int interval = int.Parse(intervals[0].ToString());
                        string unit = intervals[1].ToString().ToUpper();

                        if (getLastScanningObj == "")
                            unit = "X";

                        switch (unit)
                        {
                            case "M":
                                TimeSpan timeSpanM = DateHelper.Diff(serverDate.ToString(), getLastScanningObj);
                                if (timeSpanM.TotalMinutes > interval)
                                    findSQL=string.Format("select * from  {0}  ;", rule.ModelName);
                                break;
                            case "D":
                                TimeSpan timeSpanD = DateHelper.Diff(serverDate.ToString(), getLastScanningObj);
                                if (timeSpanD.TotalDays > interval)
                                    findSQL = string.Format("select * from  {0}  ;", rule.ModelName);
                                break;
                            default:
                                    findSQL = string.Format("select * from  {0}  ;", rule.ModelName);
                                break;
                        }

                        
                        break;
                    }
            }
            try
            {
                if (findSQL == "")
                    return null;
                DataTable dt = WMSqlExcuter.ExecuteDataTable(findSQL);   //扫描新插入的数据
                
                return dt;
            }
            catch (Exception exp)
            {
                return null;
            }
        }

        //处理进程
        public static void ProcessRule(DataTable dt, ModelRule rule, string from)
        {
            foreach (DataRow row in dt.Rows)
            {
                try
                {
                    if (rule.Processor.ShouldWork(row))
                    {
                        iProcessState = rule.Processor.Process(row);
                        if (from == "new")
                        {
                            if (iProcessState != 0)
                            {
                                InsertErrorDT(dt, rule.Name);
                                LoggerManager.GetLogger().Error(string.Format("{0} {1}", rule.Name, "处理时发生异常"));
                            }
                        }
                        else
                        {
                            if (iProcessState != 0)
                            {
                                UpDateErrorData(dt);
                                LoggerManager.GetLogger().Error(string.Format("{0} {1}", rule.Name, "处理时发生异常"));
                            }
                            else
                            {
                                DelErrorData(dt);
                            }
                        }
                    }
                }
                catch (Exception ex)
                {
                    LoggerManager.GetLogger().Error(string.Format("{0} {1}", rule.Name, "处理时发生异常"), ex);
                }
            }
        }
        //异常数据记录到异常表队列中
        public static void InsertErrorDT(DataTable dt, string RuleName)
        {
            string tableName = dt.TableName;
            
            string dataKey = getUnionKyes(dt);
            if (string.IsNullOrEmpty(dataKey)) return;
            string insertSQL = string.Format("insert into sys_ren_queue (tableName,dataKey,ruleName,priority,inTime,retryTimes) values ('{0}','{1}','{2}','{3}','{4}','0') ", tableName, dataKey, RuleName, PriorityEnum.Common.ToString(), DateTime.Now);
            WMSqlExcuter.ExecuteRealNonQuery(insertSQL);
        }

        //更新异常数据表
        public static void UpDateErrorData(DataTable dt)
        {
            string tableName = dt.TableName;
            string dataKey = getUnionKyes(dt);
            if (string.IsNullOrEmpty(dataKey)) return;
            string checkRetryTimesSQL = string.Format("select  retryTimes from sys_ren_queue where tableName='{0}' and dataKey='{1}' ", tableName, dataKey);
            int retryTimes = int.Parse(WMSqlExcuter.ExecuteRealScalar(checkRetryTimesSQL).ToString());
            if (retryTimes >= SysRuleConst.errorProcessTimes - 1)
            {
                DelErrorData(dt);
            }
            else
            {
                string updateSQL = string.Format(" update sys_ren_queue set  retryTimes=retryTimes+1 where tableName='{0}' and  dataKey='{1}' ", tableName, dataKey);
                WMSqlExcuter.ExecuteRealNonQuery(updateSQL);
            }
        }

        //删除异常数据
        private static void DelErrorData(DataTable dt)
        {
            string tableName = dt.TableName;
            string dataKey = getUnionKyes(dt);
            if (string.IsNullOrEmpty(dataKey)) return;
            string deleteSQL = string.Format(" delete from  into sys_ren_queue where tableName='{0}' and  dataKey='{1}' ", tableName, dataKey);
            WMSqlExcuter.ExecuteRealNonQuery(deleteSQL);
        }

        // 拼接联合字段值 （处理联合主键）
        private static string getUnionKyes(DataTable dt)
        {
            string unionKyes = string.Empty;
            DataColumn[] keyColumnNames = dt.PrimaryKey;
            //string[] KeyColumnNames = dm.KeyColumnName.Split(',');
            foreach (DataColumn columnName in keyColumnNames)
            {
                unionKyes += dt.Rows[0][columnName.ColumnName].ToString() + "|";
            }
            if (!string.IsNullOrEmpty(unionKyes))
            {
                unionKyes = unionKyes.Substring(0, unionKyes.Length - 1);
            }
            return unionKyes;
        }

        // 拼接联合字段值 （处理联合主键）
        private static string getUnionKyes(string tableName)
        {
            string unionKyes = string.Empty;
            string findSQL = string.Format("select top 1 * from  {0} ; ", tableName);
            DataTable dt = WMSqlExcuter.ExecuteDataTable(findSQL);
            DataColumn[] keyColumnNames = dt.PrimaryKey;
            //string[] KeyColumnNames = dm.KeyColumnName.Split(',');
            foreach (DataColumn columnName in keyColumnNames)
            {
                unionKyes += dt.Rows[0][columnName.ColumnName].ToString() + "|";
            }
            if (!string.IsNullOrEmpty(unionKyes))
            {
                unionKyes = unionKyes.Substring(0, unionKyes.Length - 1);
            }
            return unionKyes;
        }
    }
}
